/**
 * @class UIElements.Keyboard.KeyboardControlBase
 * @extends UIElements._base
 * @ignore
 * Abstract Class<br>
 * Deploys or not a keyboard according to target's version
 */
dojo.provide("UIElements.Keyboard.KeyboardControlBase");
dojo.declare("UIElements.Keyboard.KeyboardControlBase",
    [UIElements._base, dojox.dtl._Templated],
    {
        /* --------------------------------- Public attributes ------------------------------------ */

        /**
         * Contains the keys of the keyboard
         * @property
         * @type Array
         */
        arrKeys: [ ],


        arrEmoIcon: null,

        /**
         * Contains the dom element keys of the keyboard
         * @property
         * @type Array
         */
        domArrKeys: [ ],
        /**
         * rows of buttons
         * @property
         * @type Array
         */

        domIMEOtherButtons: [ ],
        /**
         * i18n path for translation
         * @property
         * @type Array
         */
        i18n: "UIElements.Keyboard",
        /**
         * InputField of the keyboard
         * @property
         * @type UIElements.InputField.InputField
         */
        objInputField: null,
        /**
         * OK button
         * @property
         * @type UIElements.Keyboard.Key
         */
        objOkButton: null,
        /**
         * Exit button
         * @property
         * @type UIElements.Keyboard.Key
         */
        objExitButton: null,

        /**
         * defines the type of the keyboard to display
         * values : num, alphaNum, filter, filterNDial, emoticons, password
         * @property
         * @type String
         */
        strType: "",

        /**
         * defines the title  of the input field to display
         * @property
         * @type String
         */
        strInputTitle: null,

        /**
         * specifies if caller is multiLines (so will be the keyboard's inputfield)
         * @property
         * @type Boolean
         */
        bMultiLines: false,
        /**
         * Keyboard 'language' (azerty / qwerty / qwertz)
         * @property
         * @type String
         */
        strLang: "",


        /**
         * If true, in password mode, hide stars if password is empty
         * @property
         * @type Boolean
         */
        hideStarsIfPasswordEmpty: false,

        /**
         * Allow the keyboard services to display or remove some buttons. The goal is that we only have to handle 2 types:
         * - numeric keyboard
         * - alphaNumeric keyboard
         * @property
         * @type Object
         * Example :
         this.keyboardFlags = {
		 	"dialByName": false,
		 	"gotoContact": false,
		 	"cancel": true,
		 	"ok": true,
		 	"backspace": true,
		 	"password": false,
		 	"emoticon": false,
		 	"filter": false,
		 	"byDialpad": false,
		 	"multiline": false,
		 	"gotoCommunication": false,
		 	"gotoProgkeys": false,
			"gotoDirectory": false,
			"dialSpace": false
			"imeIndicator": false
		  };
         */
        keyboardFlags: null,
        /**
         * keymap for each Keyboard 'language' (azerty / qwerty / qwertz)
         * @property
         * @type Array
         */
        languages: {},
        /**
         * Button OK label
         * @property
         * @type String
         */
        strOkLabel: null,
        /**
         * Button Exit label
         * @property
         * @type String
         */
        strExitLabel: null,
        /**
         * Button OK icon
         * @property
         * @type String
         */
        strOkIcon: null,
        /**
         * Button Exit icon
         * @property
         * @type String
         */
        strExitIcon: null,
        /*
         * initial mode for alphanumeric keyboard (ICTouchAPI.KeyboardAlphanumMode enumeration)
         * @property
         * @type String
         */
        strDefaultMode: null,

        objIMEInputField: "",
        //Hi Soft
        boolIMEMode: 1,
        arrLines: [],
        arrCandidates: [],
        //Hi Soft
        candidatesPage: 1,
        candidatesPageSize: 8,

        /* --------------------------------- Private attributes ----------------------------------- */

        /**
         * @private
         */
        attributeMap: {
            strInputTitle: {
                node: "domInputTitle",
                type: "innerHTML"
            }
        },

        /**
         * @private
         */
        numLockKeyboard: 0,
        /**
         * @private
         * This are the default value when no flags are given, user flags will override them
         */
        _defaultKeyboardFlags: {
            "dialByName": false,
            "gotoContact": false,
            "cancel": true,
            "ok": true,
            "backspace": true,
            "password": false,
            "emoticon": false,
            "filter": false,
            "byDialpad": false,
            "multiline": false,
            "gotoCommunication": false,
            "gotoProgkeys": false,
            "international": false,
            "gotoDirectory": false,
            "latest": false,
            "dialSpace": false,
            "hotelMode": false,
            "customKeysUpdated": false,
            "actionButtonInDialpad": 0,
            //Hi Soft
            "imeIndicator": true

        },

        /**
         * @private
         * Associative array defining internal flags :
         * (bool) _flags["star"] if the star key is on and active
         * (bool) _flags["emoticon"] if the emoticon is on and active
         * (bool) _flags["emoticonLock"] if the emoticon is on and locked
         * (bool) _flags["caps"] if the caps key is on and active
         * (bool) _flags["capsLock"] if the caps key is on, active and locked.
         * (bool) _flags["starKey"] is the key defining which extended keyboard is displayed.
         * (bool) _flags["numLock"] if the alphaNum's numpad is active and locked.
         * (bool) _flags["ime"] if the ime is required
         */
        _flags: {},
        /**
         * @private
         */
        _strClassToAdd: null,
        /**
         * @private
         * International button
         */
        _objInternational: null,

        /**
         * @private
         * Space button
         */
        _objSpace: null,

        _isPasswordKeyBoard: false,
        /* ------------------------------------ Constructor --------------------------------------- */

        constructor: function () {
            // Init all attributes so they won't be shared between instances.
            this.arrKeys = [ ];
            this.domArrKeys = [ ];
            this.domIMEOtherButtons = [ ];
            this.arrLines = [ ];
            //Hi Soft
            for (var i = 0; i < this.candidatesPageSize; ++i) {
                this.arrCandidates.push("&nbsp;");
            }

            this.strLang = "azerty_french";
            this._setDefaultModeFlags();

            ICTouchAPI.settingServices.subscribeToSetting(this, "Defaultkeyboard", this._gotDefaultKeyboard);

            this.keyboardFlags = {};
        },

        postMixInProperties: function () {
            // Initialise Ok and Exit button label and icon
            this._setOkExitLabels(this.strOkLabel, this.strExitLabel);
            this._setOkExitIcons(this.strOkIcon, this.strExitIcon)
            this.refreshFlags();
            // Generate keyboard
            this._loadKeyMaps();
            if (this._defaultKeyboardFlags.emoticon !== this.keyboardFlags.emoticon) {
                this._loadEmoMap("emoticon");
            }
            this.arrKeys = this._getKeyboardSkeleton(this.strType);
            var lineLength = this.strType == "num" ? 5 : 13;
            var remainingWidth = lineLength; // How many space is still left on the current row ( arrLine )
            var arrLine = []; // This contain the current line of widget for the template
            var arrWidthToSkip = []; // This contain how many cell should be ignored due to rowspan
            var i, j;

            /*
             In the template we need rows of buttons ( arrLines ) but in javascript we only have a big key array ( arrKeys ).
             So for each row we must compute how many buttons fits in taking into account colspan and rowspan.
             */
            for (i = 0; i < this.arrKeys.length; i++) {
                var currentKey = this.arrKeys[i];

                /*
                 If the last button did fill the row we need to create the next row
                 Using "while" because we could have a line filled only with rowspan
                 for exemple when every button got a 2 height size
                 */
                while (remainingWidth <= 0) {
                    arrWidthToSkip.shift();
                    remainingWidth = lineLength;
                    // Substracting the space taken by rowspan from the last row
                    if (typeof arrWidthToSkip[0] === "number") {
                        remainingWidth -= arrWidthToSkip[0];
                    }
                    this.arrLines.push(arrLine);
                    arrLine = [];
                }

                // Size of the button
                currentKey.intInputWidth = (currentKey.intInputWidth || 1);
                currentKey.intInputHeight = (currentKey.intInputHeight || 1);

                // Substract remaining space
                remainingWidth -= currentKey.intInputWidth;
                arrLine.push(currentKey);

                // When the button got height, increment the array for the next rows
                for (j = 1; j < currentKey.intInputHeight; ++j) {
                    arrWidthToSkip[j] = arrWidthToSkip[j] || 0;
                    arrWidthToSkip[j] += currentKey.intInputWidth;
                }

                // Change the attributes of the currentKey
                currentKey.objElement.attr("intWidth", currentKey.intInputWidth);
                currentKey.objElement.attr("intHeight", currentKey.intInputHeight);
            }
            this.arrLines.push(arrLine);
        },

        postCreate: function () {
            var i;
            dojo.addClass(this.domNode, this._strClassToAdd);
            for (i = 0; i < this.arrKeys.length; ++i) {
                this.arrKeys[i].objElement.placeAt(this.domArrKeys[i]);
            }
            if (this.domIMEOther) {
                this.scroll = FXScroll.init({
                    "handler": this.domIMEOther,
                    "easing": true
                });
            }

            // getSetting moved from constructor to postCreate because of a bug when preloading qwerty keyboard linked
            // to asynchronous call of the getSetting callback
            ICTouchAPI.settingServices.getSetting("Defaultkeyboard", this, this._gotDefaultKeyboard);
            this.setKeyboardFlags(this.keyboardFlags, true);
            this.setMode(this.strDefaultMode)
            this.placeTitle();
        },

        /* ----------------------------------- Getter / Setter------------------------------------- */

        /**
         * Get application name
         */
        getApplicationName: function () {
            return "KeyboardControl";
        },

        setTitle: function (strTitle) {
            if (this.strInputTitle != strTitle) {
                this.strInputTitle = strTitle;
                this.placeTitle();
            }
        },

        placeTitle: function () {
            if (this.strInputTitle && this.strInputTitle.isI18Ned) {
                this.domInputTitle.innerHTML = this.strInputTitle.getTranslation();
            }
            else {
                this.domInputTitle.innerHTML = this.strInputTitle;
            }
        },

        /**
         * Set the keyboard flags.
         * This will add or remove button according to the flags. With some flags, put the order position instead of true
         * If the flag isn't set it will revert back to its default value
         * @param {Object} flags Associative array with the new flags
         * <pre><code>
         setKeyboardFlags({
		 		"dialByName": false,		//button order, num keyboard only
		 		"gotoContact": false,		//button order, num keyboard only
		 		"cancel": true,
		 		"ok": true,
		 		"backspace": true,
		 		"password": false,
		 		"emoticon": false,
		 		"filter": false,
		 		"byDialpad": false,
		 		"multiline": false,
				"gotoCommunication": false,	//button order, num keyboard only
				"gotoProgkeys": false,
				"international": false,
				"dialSpace": false,
				"hotelMode": false,
				"customKeysUpdated" : false,
				"actionButtonInDialpad" : 0
		  },true);
         </pre></code>
         * @param {Boolean} _first
         */
        setKeyboardFlags: function (flags, _first) {
            // _first is only set from inside KeyboardControl and should only called from postCreate or any of its subroutines
            // Clone the defaults flags so we don't change it
            var newFlags = dojo.clone(this._defaultKeyboardFlags);
            dojo.mixin(newFlags, flags);
            if (this.strType == "num") {
                // If any flag has changed, we must reorder everything
                if (_first ||
                    newFlags.gotoCommunication !== this.keyboardFlags.gotoCommunication ||
                    newFlags.gotoProgkeys !== this.keyboardFlags.gotoProgkeys ||
                    newFlags.gotoContact !== this.keyboardFlags.gotoContact ||
                    newFlags.dialByName !== this.keyboardFlags.dialByName ||
                    newFlags.gotoDirectory !== this.keyboardFlags.gotoDirectory ||
                    newFlags.latest !== this.keyboardFlags.latest ||
                    newFlags.hotelMode
                    ) {
                    if (newFlags.hotelMode) {// if we are in hotel mode, create custom keys
                        var customKeys = ICTouchAPI.keyboardServices.getDialpadKeys();
                        var intIndex = 0;
                        var intKeyboardIndex = 0;
                        if (newFlags.customKeysUpdated || (newFlags.customKeys && !this.keyboardFlags.customKeys)) {
                            for (intIndex = 0; intIndex < 8; intIndex++) {
                                var customKey = ICTouchAPI.keyboardServices.getCustomKey(customKeys[intIndex]);
                                intKeyboardIndex = intIndex < 4 ? 5 * intIndex + 1 : (intIndex - 3) * 5;
                                if (customKey && customKeys[intIndex] != null) {
                                    this._replaceButton(intKeyboardIndex, true, null, customKey);
                                }
                                else {
                                    // if the button has been removed, replace it by an empty button
                                    this._replaceButton(intKeyboardIndex, false, null, null);
                                }
                            }
                            switch (newFlags.actionButtonInDialpad) {
                                case 0:
                                    this._replaceButton(23, false, null, null);
                                    break;
                                case 1:
                                    this._replaceButton(23, true, this.createDialByName);
                                    break;
                                case 2:
                                    this._replaceButton(23, true, this.createGotoDirectory);
                                    break;
                                case 3:
                                    if (customKeys[8]) {
                                        this._replaceButton(23, true, null, ICTouchAPI.keyboardServices.getCustomKey(customKeys[8]));
                                    }
                                    else {
                                        this._replaceButton(23, false, null, null);
                                    }
                                    break;
                            }
                        }
                        else if (!newFlags.customKeys && this.keyboardFlags.customKeys) {
                            for (intIndex = 0; intIndex < 8; intIndex++) {
                                intKeyboardIndex = intIndex < 4 ? 5 * intIndex + 1 : (intIndex - 3) * 5;
                                this._replaceButton(intKeyboardIndex, false, null, null);
                            }
                        }

                    }
                    else {
                        var itemToTest = [];
                        if (newFlags.gotoCommunication) itemToTest[newFlags.gotoCommunication] = true;
                        if (newFlags.gotoProgkeys) itemToTest[newFlags.gotoProgkeys] = true;
                        if (newFlags.gotoContact) itemToTest[newFlags.gotoContact] = true;
                        if (newFlags.dialByName) itemToTest[newFlags.dialByName] = true;
                        if (newFlags.gotoDirectory) itemToTest[newFlags.gotoDirectory] = true;
                        if (newFlags.latest) itemToTest[newFlags.latest] = true;
                        var firstPosition = 23;
                        var position = firstPosition;
                        for (var i = itemToTest.length - 1; i >= 0; i--) {
                            if (itemToTest[i] != null) {
                                if (newFlags.latest === i) {
                                    this._replaceButton(position, true, this.createShowLatestNumber);
                                    position = this._getPreviousPosition(newFlags.latest, position);
                                }
                                if (newFlags.gotoCommunication === i) {
                                    this._replaceButton(position, true, this.createGotoCommunication);
                                    position = this._getPreviousPosition(newFlags.gotoCommunication, position);
                                }
                                if (newFlags.gotoDirectory === i) {
                                    this._replaceButton(position, true, this.createGotoDirectory);
                                    position = this._getPreviousPosition(newFlags.gotoDirectory, position);
                                }
                                if (newFlags.dialByName === i) {
                                    this._replaceButton(position, true, this.createDialByName);
                                    position = this._getPreviousPosition(newFlags.dialByName, position);
                                }
                                if (newFlags.gotoContact === i) {
                                    this._replaceButton(position, true, this.createGotoContact);
                                    position = this._getPreviousPosition(newFlags.gotoContact, position);
                                }
                                if (newFlags.gotoProgkeys === i) {
                                    this._replaceButton(position, true, this.createGotoProgkeys);
                                    position = this._getPreviousPosition(newFlags.gotoProgkeys, position);
                                }
                            }
                        }
                    }


                    // Fill the rest with empty buttons
                    if (!newFlags.hotelMode) {
                        while (position >= 5) {
                            this._replaceButton(position, false, null);
                            position = this._getPreviousPosition(true, position);
                        }
                    }
                }

                // Backspace button is integrated into InputField
                if (newFlags.backspace !== this.keyboardFlags.backspace || _first) {
                    var callback = newFlags.backspace ? dojo.hitch(this, this._backspaceCallback) : null;
                    this.getInputField().setExtraButton(callback, true, _("delete", this.i18n), "delete");
                }
                // Enable the '+' key when international mode is enabled
                if (newFlags.international !== this.keyboardFlags.international || _first) {
                    // this._internationalKeyPressed is a special case, it don't need context
                    var callback = newFlags.international ? this._internationalKeyPressed : null;
                    this._objInternational.objElement.setLongCallback(callback);
                }
                // Enable the ' ' key on dialpad 1
                if (newFlags.dialSpace !== this.keyboardFlags.dialSpace || _first) {
                    var callback = newFlags.dialSpace ? this._spaceKeyPressed : null;
                    this._objSpace.objElement.setLongCallback(callback);
                }
            } else if (this.strType == "alphaNum") {
                // Flags only meaningful in alphanum keyboard
                // crms00334755: dial-pad button not displayed anymore in keyboards / let in comment just in case of new ergo requirements...
                /*else if( newFlags.byDialpad !== this.keyboardFlags.byDialpad || _first ) {
                 this._replaceButton(4, newFlags.byDialpad, this.createByDialpad);
                 }*/
                // Multiline support
                if (newFlags.multiline !== this.keyboardFlags.multiline || _first) {
                    this._replaceButton(30, newFlags.multiline, this.createCarriageReturn);
                    // Set multiline InputField
                    this.getInputField().setMultiline(newFlags.multiline);
                }
                if (newFlags.backspace !== this.keyboardFlags.backspace || _first) {
                    this._replaceButton(17, newFlags.backspace, this.createBackspace);
                }
            }
            if (newFlags.cancel !== this.keyboardFlags.cancel || _first) {
                var pos = this.strType === "num" ? 21 : 31;
                this._replaceButton(pos, newFlags.cancel, this.createCancel);
            }
            if (newFlags.ok !== this.keyboardFlags.ok || _first) {
                var pos = this.strType === "num" ? 22 : 41;
                this._replaceButton(pos, newFlags.ok, this.createOk);
            }
            if (newFlags.password !== this.keyboardFlags.password) {
                this.getInputField().setPassword(newFlags.password);
            }
            this.keyboardFlags = newFlags;
        },

        /**
         * Set the alphaNumeric keyboard mode.
         * This will change alphaNumeric keyboard into lowercase, uppercase and numeric mode.         *
         * @param {String} strMode Name of the mode (ICTouchAPI.KeyboardAlphanumMode enumeration) (default: NORMAL)
         */
        setMode: function (strMode) {
            if (this.strType !== "alphaNum") {
                return;
            }
            else if (strMode === ICTouchAPI.KeyboardAlphanumMode.NORMAL) {
                this._setDefaultModeFlags();
            }
            else if (strMode === ICTouchAPI.KeyboardAlphanumMode.CAPS) {
                this._setDefaultModeFlags();
                this._flags["caps"] = 1;
            }
            else if (strMode === ICTouchAPI.KeyboardAlphanumMode.CAPS_LOCK) {
                this._setDefaultModeFlags();
                this._flags["caps"] = 1;
                this._flags["capsLock"] = 1;
            }
            else if (strMode === ICTouchAPI.KeyboardAlphanumMode.NUMERIC) {
                this._setDefaultModeFlags();
                this._flags["numLock"] = 1;
            }
            else {
                this._setDefaultModeFlags();
            }
            this._swapKeyMap();
            this._updateButtonsIcons();
        },

        /**
         * Get the actual InputField Element
         * This should only be called by the keyboardServices
         * @return {UIElements.InputField.InputFieldControl} the InputField of this keyboard
         */
        getInputField: function () {
            return this.objInputField.objElement;
        },

        /* ----------------------------------- Public methods ------------------------------------- */

        /**
         * Refresh flag boolIMEMode
         */
        refreshFlags: function () {
            this._flags["ime"] = this.boolIMEMode;
        },

        /**
         * this is called when the keyboard has been inserted into toaster
         * So we steal focus and place it on our InputField. A problem arise when preloading keyboards
         * so we scroll back the body.
         */
        placeAt: function () {
            this.inherited(arguments);
            this.focus();
            document.body.scrollTop = 0;
        },

        /**
         * Replace the current icon of OK button and EXIT button.
         * @param {String} strOkIcon button OK icon
         * @param {String} strExitIcon button Exit icon
         */
        updateOkExitButtonIcons: function (strOkIcon, strExitIcon) {
            this._setOkExitIcons(strOkIcon, strExitIcon);
            if (this.objOkButton && this.objOkButton.objElement && this.keyboardFlags.ok) {
                this.objOkButton.objElement.attr("strButtonIcon", this.strOkIcon);
            }
            if (this.objExitButton && this.objExitButton.objElement && this.keyboardFlags.cancel) {
                this.objExitButton.objElement.attr("strButtonIcon", this.strExitIcon);
            }
        },

        /**
         * When this keyboard takes focus that means that the user can type with physical keyboard into InputField
         */
        focus: function () {
            this.getInputField().getInputFieldObject().focus();
        },

        /**
         * Replace the current label of OK button and EXIT button.
         * @param {String} strOkLabel button OK label
         * @param {String} strExitLabel button Exit label
         */
        updateOkExitButtonLabels: function (strOkLabel, strExitLabel) {
            this._setOkExitLabels(strOkLabel, strExitLabel);
            if (this.objOkButton && this.objOkButton.objElement && this.strOkLabel && this.keyboardFlags.ok) {
                this.objOkButton.objElement.replaceText(this.strOkLabel);
            }
            if (this.objExitButton && this.objExitButton.objElement && this.strExitLabel && this.keyboardFlags.cancel) {
                this.objExitButton.objElement.replaceText(this.strExitLabel);
            }
        },

        /**
         * Create keyboard button "international"
         */
        createInternational: function () {
            var context = this;
            var key = {
                intInputWidth: 1,
                intInputHeight: 1,
                objElement: new UIElements.Keyboard.Key({
                    strButtonType: 'letter',
                    strButtonText: '0',
                    strSubText: '+',
                    strButtonName: "keyboard_International",
                    intInputWidth: 3,
                    intInputHeight: 1,
                    funcCallback: function (value) {
                        context.keyboardLetterCallback(value);
                    },
                    funcCallbackLong: null, // Will be defined by the keyboardFlags
                    funcCallbackRelease: function (value) {
                        ICTouchAPI.keyboardServices.keyReleased(value);
                    }
                })
            }
            return key;
        },

        _internationalKeyPressed: function () {
            ICTouchAPI.keyboardServices.keyPressed('+');
        },

        /**
         * Create keypad button "space"
         */
        createSpace: function () {
            var context = this;
            var key = {
                intInputWidth: 1,
                intInputHeight: 1,
                objElement: new UIElements.Keyboard.Key({
                    strButtonType: 'letter',
                    strButtonText: '1',
                    strSubText: '&#9251;',
                    strButtonName: "dialPad1",
                    intInputWidth: 3,
                    intInputHeight: 1,
                    funcCallback: function (value) {
                        context.keyboardLetterCallback(value);
                    },
                    funcCallbackLong: null, // Will be defined by the keyboardFlags
                    funcCallbackRelease: function (value) {
                        ICTouchAPI.keyboardServices.keyReleased(value);
                    }
                })
            }
            return key;
        },
        _spaceKeyPressed: function () {
            ICTouchAPI.keyboardServices.keyPressed(' ');
        },

        /**
         * Create keyboard button "Dial By Name"
         */
        createDialByName: function () {
            return this.createButton({
                intInputWidth: 1,
                intInputHeight: 1,
                strButtonText: _("Search", "ICTouchAPI"),
                strButtonIcon: "generic-search-by-name",
                strButtonName: "keyboard_search",
                boolAttachParent: true,
                callbackClick: function () {
                    ICTouchAPI.keyboardServices.doCustomCallback("keyboard");
                }
            });
        },

        /**
         * Create keyboard button "Go to contact"
         */
        createGotoContact: function () {
            return this.createButton({
                intInputWidth: 1,
                intInputHeight: 1,
                strButtonText: _("contacts", this.i18n),
                strButtonIcon: "contacts-application",
                strButtonName: "keyboard_contact",
                boolAttachParent: true,
                callbackClick: function () {
                    ICTouchAPI.keyboardServices.doCustomCallback("goto-contact");
                }
            });
        },

        /**
         * Create keyboard button "Go to communication"
         */
        createGotoCommunication: function () {
            return this.createButton({
                intInputWidth: 1,
                intInputHeight: 1,
                strButtonText: _("communication", this.i18n),
                strButtonIcon: "communication-application",
                strButtonName: "keyboard_communication",
                boolAttachParent: true,
                callbackClick: function () {
                    ICTouchAPI.keyboardServices.doCustomCallback("goto-communication");
                }
            });
        },

        /**
         * Create keyboard button "Go to communication"
         */
        createGotoProgkeys: function () {
            return this.createButton({
                intInputWidth: 1,
                intInputHeight: 1,
                strButtonText: _("favorites", this.i18n),
                strButtonIcon: "contacts-favorites-application",
                strButtonName: "keyboard_progkeys",
                boolAttachParent: true,
                callbackClick: function () {
                    ICTouchAPI.keyboardServices.doCustomCallback("goto-progkeys");
                }
            });
        },

        /**
         * Create keyboard button "Go to Directory"
         */
        createGotoDirectory: function () {
            return this.createButton({
                intInputWidth: 1,
                intInputHeight: 1,
                strButtonText: _("directory", this.i18n),
                strButtonIcon: "sdk-hotel-directory",
                strButtonName: "keyboard_directory",
                boolAttachParent: true,
                callbackClick: function () {
                    ICTouchAPI.keyboardServices.doCustomCallback("goto-contact");
                }
            });
        },


        /**
         * Create keyboard button "Show latest numbers"
         */
        createShowLatestNumber: function () {
            return this.createButton({
                intInputWidth: 1,
                intInputHeight: 1,
                strButtonText: _("Latest", this.i18n),
                strButtonIcon: "userservices-latest-numbers",
                strButtonName: "keyboard_latest",
                boolAttachParent: true,
                callbackClick: function () {
                    ICTouchAPI.keyboardServices.doCustomCallback("show-latest");
                }
            });
        },

        // Create keyboard button

        /**
         * Create keyboard button "Filter"
         */
        createFilter: function () {
            return this.createButton({
                strButtonText: _("filter", this.i18n),
                strButtonIcon: "generic-filter",
                strButtonName: "keyboard_FILTER",
                callback: function () {
                    ICTouchAPI.keyboardServices.doCustomCallback("filter");
                }
            });
        },

        /**
         * Create keyboard button "Dialpad"
         * crms00334755: dial-pad button not displayed anymore in keyboards / let in comment just in case of new ergo requirements...
         */
        /*createByDialpad: function() {
         return this.createButton({
         strButtonIcon: "dialpad",
         strButtonName: "keyboard_DIAL",
         callback: function () {
         ICTouchAPI.keyboardServices.doCustomCallback("dial-pad");
         }
         });
         },*/


        /**
         * Create an inputField UIElement with specified args.
         * There must be only one inputField, its reference will be stored in this.objInputField
         * @param {Array} args array with desired arguments to pass to the UIElement.
         *    intInputWidth {Int} the width of the key
         *    strInputText {String} the default text to display in the inputField
         *    strInputIcon {String} name of the icon to place before the inputField
         * @return {UIElement.InputField} this.objInputField
         * <pre><code>
         this.createInputField({
		 			intInputWidth	: 9,
		 			strInputText	: this.strInputText,
		 			strInputIcon	: 'notif-16px-denied'
		 		}),
         </pre></code>
         */
        createInputField: function (args) {
            this.objInputField = {
                intInputWidth: args.intInputWidth || null,
                intInputHeight: args.intInputHeight || null,
                objElement: new UIElements.InputField.InputFieldControl({
                    strInputText: args.strInputText,
                    strInputContent: args.strInputText,
                    strInputIcon: args.strInputIcon,
                    boolPassword: args.boolPassword,
                    hideStarsIfPasswordEmpty: args.hideStarsIfPasswordEmpty
                })
            };
            return this.objInputField;
        },

        /**
         * Create a backspace key from SimpleButton UIElement.
         * @param {Array} args array with desired arguments to pass to the UIElement.
         *    intInputWidth {Int} the width of the key
         *    strButtonType {String} By default, a backspace key is of TextButton type
         *    strButtonText {String} Name of the backspace key
         * @return the object
         * <pre><code>
         this.createBackspace({})
         </pre></code>
         */
        createBackspace: function (args) {
            var context = this;
            return        {
                intInputWidth: args.intInputWidth || 1,
                intInputHeight: args.intInputHeight || 1,
                objElement: new UIElements.Keyboard.Key({
                    strButtonIcon: "delete",
                    strButtonName: "keyboard_DEL",
                    strButtonText: null,
                    boolAutorepeat: true,
                    // Both needed because the size of the icon ( 32 or 64 ) is computed on creation
                    intWidth: args.intInputWidth || 1,
                    intHeight: args.intInputHeight || 1,

                    funcCallback: dojo.hitch(context, context._backspaceCallback),
                    funcCallbackRelease: function () {
                        ICTouchAPI.keyboardServices.keyReleased('backspace');
                    }
                })
            }
        },

        /**
         * Could it be a lack of understanding of how javascript builds its objects,
         * but that function had to be defined outside of the key's scope but inside this
         * createPrevNextButton so it is accessible from within the objects 'attributes' and methods
         * during creation.
         * It needs no parameter and returns true or false wether the icon + callback have to be a carriage return.
         * @return {bool} if true, display the carriage Return, else does not display it.
         */
        createCarriageReturn: function () {
            return {
                intInputWidth: 1,
                intInputHeight: 1,
                objElement: new UIElements.Keyboard.Key({
                    strButtonIcon: "return",
                    funcCallback: function () {
                        ICTouchAPI.keyboardServices.keyPressed("\n");
                    }
                })
            };
        },

        //Hi Soft
        /*
         * Create IME indicator key
         * @param {Array} args array with desired arguments to pass to the UIElement.
         *    intInputWidth {Int} the width of the key
         *    strButtonType {String} By default, a backspace key is of TextButton type
         *    strButtonText {String} Name of the backspace key
         * @return the object
         * <pre><code>
         this.createBackspace({})
         </pre></code>
         * */
        createIMEIndicator: function (args) {
            var context = this;
            this.imeIndicatorLabel = this.imeIndicatorLabel || "EN";
            this.objIMEIndicatorButton = {
                intInputWidth: args.intInputWidth || 1,
                intInputHeight: args.intInputHeight || 1,
                objElement: new UIElements.Keyboard.Key({
//                    strButtonIcon: "delete",
                    strButtonName: "keyboard_IME_Indicator",
                    strButtonText: this.imeIndicatorLabel,
                    boolAutorepeat: false,
                    // Both needed because the size of the icon ( 32 or 64 ) is computed on creation
                    intWidth: args.intInputWidth || 1,
                    intHeight: args.intInputHeight || 1,

                    funcCallback: dojo.hitch(context, context._imeIndicatorCallback),
                    funcCallbackRelease: function () {
                        ICTouchAPI.keyboardServices.keyReleased('imeIndicator');
                    }
                })
            };
            return this.objIMEIndicatorButton;
        },
        //Hi Soft
        IMEPressOK: function () {
            // if OK pressed in IME mode
            var isIMELang = ICTouchAPI.keyboardServices.isAPACIMELang(this.strLang);
            if (!isIMELang || 'apac_en' == this.strLang || !this.domIMEInput.value) return false;

            for (var index in this.domIMEInput.value) this.getInputField().addNewChar(this.domIMEInput.value[index]);
            this.clearIMEInput();
            ICTouchAPI.IMEServices.resetCharSequence();
            this.recreateCandidateList("");
            return true;
        },
        /**
         * Create keyboard button "OK"
         * <pre><code>
         createOK({
		 		intInputHeight: "50",
		 		intInputWidth : "100",
		 		strButtonIcon: "generic-ok",
		 		strButtonText: "OK",
		 		strCustomClass: "ok",
		 		strButtonName: 'keyboard_OK',
		 		callback: function () {
		 			ICTouchAPI.keyboardServices.closeKeyboard(ICTouchAPI.keyboardServices.CLOSE_ACTION_OK);
		 		}
		 	}
         );

         </pre></code>
         */
        createOk: function (args) {
            var that = this;
            this.objOkButton = this.createButton({
                intInputHeight: args.intInputHeight || 1,
                intInputWidth: args.intInputWidth || 1,
                strButtonIcon: "generic-ok",
                strButtonText: this.strOkLabel,
                strCustomClass: "ok",
                boolAttachParent: true,
                strButtonName: 'keyboard_OK',
                callbackClick: function () {
                    if (that.IMEPressOK()) return;
                    if (that._flags["emoticon"] || that._flags["emoticonLock"]) {
                        that._flags["emoticon"] = false;
                        that._flags["emoticonLock"] = false;
                    }
                    ICTouchAPI.keyboardServices.closeKeyboard(ICTouchAPI.keyboardServices.CLOSE_ACTION_OK);
                }
            });
            return this.objOkButton;
        },

        /**
         * Create keyboard button "Cancel"
         * <pre><code>
         createCancel({
		 		intInputHeight: "50",
		 		intInputWidth : "100",
		 		strButtonIcon: "generic-cancel",
		 		strButtonText: "Cancel",
		 		strCustomClass: "Cancel",
		 		strButtonName: 'keyboard_Cancel',
		 		callback: function () {
		 			ICTouchAPI.keyboardServices.closeKeyboard(ICTouchAPI.keyboardServices.CLOSE_ACTION_CANCEL);
		 		}
		 	}
         );
         </pre></code>
         */
        createCancel: function (args) {
            var that = this;
            this.objExitButton = this.createButton({
                intInputHeight: args.intInputHeight || 1,
                intInputWidth: args.intInputWidth || 1,
                strButtonIcon: "generic-cancel",
                strButtonText: this.strExitLabel,
                strCustomClass: "cancel",
                strButtonName: "keyboard_EXIT",
                // Don't make whole Cancel button sensitive in numeric mode
                boolAttachParent: true,
                callbackClick: function () {
                    if (that._flags["emoticon"] || that._flags["emoticonLock"]) {
                        that._flags["emoticon"] = false;
                        that._flags["emoticonLock"] = false;
                    }
                    ICTouchAPI.keyboardServices.closeKeyboard(ICTouchAPI.keyboardServices.CLOSE_ACTION_MANUAL_EXIT);
                }
            });
            return this.objExitButton;
        },

        /**
         * Create a Key for the keyboard UIElement.
         * @param {Array} args array with desired arguments to pass to the UIElement.
         *    intInputWidth {Int} the width of the key
         *    strButtonText {String} the button's name
         *    callback {Func} the callback function
         * @return the object
         * <pre><code>
         this.createTextButton({
		 		strButtonText: 'OK',
		 		callback: function () {
		 			ICTouchAPI.keyboardServices.closeKeyboard(true);
		 		}
		 	}),
         </pre></code>
         */
        createButton: function (args) {
            var width = args.intInputWidth || 1;
            var height = args.intInputHeight || 1;
            if (typeof args.boolAttachParent === "undefined") {
                args.boolAttachParent = true;
            }
            var objButton = {
                intInputWidth: width,
                intInputHeight: height,
                objElement: new UIElements.Keyboard.Key({
                    strButtonName: args.strButtonName,
                    strButtonText: args.strButtonText || null,
                    strButtonIcon: args.strButtonIcon || null,
                    strCustomClass: args.strCustomClass || null,
                    strNotification: args.strNotification || null,
                    boolAttachParent: args.boolAttachParent,
                    intWidth: width,
                    intHeight: height,
                    funcCallback: args.callback || null,
                    funcCallbackClick: args.callbackClick || null
                })
            };
            args = null;
            return objButton;
        },

        /**
         * Create a SpaceBar key from SimpleButton UIElement.
         * @param {Array} args array with desired arguments to pass to the UIElement.
         *    intInputWidth {Int} the width of the key
         *    strButtonType {String} By default, a SpaceBar key is of KeyboardLetter type
         *    strButtonText {String} Name of the SpaceBar key
         * @return the object
         * <pre><code>
         this.createSpaceBar({}),
         </pre></code>
         */
        createSpaceBar: function (args) {
            return {
                intInputWidth: args.intInputWidth || 3,
                objElement: new UIElements.Keyboard.Key({
                    strButtonType: 'letter',
                    strButtonText: args.strButtonText || _('SPACE', this.i18n),
                    strButtonName: args.strButtonName,
                    funcCallback: function () {
                        ICTouchAPI.keyboardServices.keyPressed(" ");
                    },
                    funcCallbackRelease: function () {
                        ICTouchAPI.keyboardServices.keyReleased(" ");
                    }
                })
            }
        },

        /**
         * Create a keyboard letter from SimpleButton UIElement.
         * @param {String} buttonName the name of the key
         * @param {String} strLetter the char to display
         * @param {String} strSubLetter the small text next to the main label
         * @param {Number} intWidth the width of the key
         * @param {Number} intHeight the width of the key
         * @return the object
         * <pre><code>
         // the #2 key, with "abc" as subletter, with a width of 3 and the name of "dialpad_3"
         this.createKeyboardLetter("2", "abc", 3, "dialpad_3")
         // the "z" letter, width a default width of 1.
         this.createKeyboardLetter("z")
         </pre></code>
         */
        createKeyboardLetter: function (buttonName, strLetter, strSubLetter, intWidth, intHeight) {
            // Save scope for callback.
            var that = this;
            intWidth = intWidth || 1;
            intHeight = intHeight || 1;
            var key = {
                intInputWidth: intWidth,
                intInputHeight: intHeight,
                emoticon: false,
                strValue: strLetter,
                objElement: new UIElements.Keyboard.Key({
                    strButtonType: 'letter',
                    strButtonText: strLetter,
                    strSubText: strSubLetter,
                    strButtonName: buttonName,
                    intInputWidth: intWidth,
                    intInputHeight: intHeight,
                    funcCallback: function (value) {
                        if (key.emoticon) {
                            that.keyboardEmoCallback(key.strValue);
                        } else {
                            that.keyboardLetterCallback(value);
                        }
                    },
                    funcCallbackRelease: function (value) {
                        ICTouchAPI.keyboardServices.keyReleased(value);
                    }
                }),
                swapContent: function (strText, strEmo) {
                    var boolEmo = strEmo != null && strEmo != '';
                    if (this.emoticon == false) {
                        if (boolEmo) {
                            this.objElement.replaceText('');
                            this.objElement.setIcon(strEmo)
                        }
                        else {
                            this.objElement.replaceText(strText);
                        }
                    } else {
                        if (boolEmo) {
                            this.objElement.replaceText('');
                            this.objElement.replaceIcon(strEmo);
                        } else {
                            this.objElement.removeIcon();
                            this.objElement.replaceText(strText);
                        }
                    }
                    this.strValue = strText;
                    this.emoticon = boolEmo;
                }
            };
            return key;
        },
        /**
         * get the text corresponding to the Emo
         */
        keyboardEmoCallback: function (strValue) {
            this.keyboardLetterCallback(strValue);
            var field = this.objInputField.objElement.getInputFieldObject();
            var intSelectionStart = field.selectionStart;
            intSelectionStart++;
            field.selectionStart = intSelectionStart;
            if (this._flags["emoticon"] && !this._flags["emoticonLock"]) {
                this._flags["emoticon"] = 0;
                this._swapKeyMap();
            }
        },

        /**
         * Could it be a lack of understanding of how javascript builds its objects,
         * but that function had to be defined outside of the key's scope but inside this
         * createPrevNextButton so it is accessible from within the objects 'attributes' and methods
         * during creation.
         * It needs no parameter and returns true or false wether the icon + callback have to be a carriage return.
         * @return {bool} if true, display the carriage Return, else does not display it.
         */
        createCarriageReturn: function () {
            return {
                intInputWidth: 1,
                intInputHeight: 1,
                objElement: new UIElements.Keyboard.Key({
                    strButtonIcon: "return",
                    funcCallback: function () {
                        ICTouchAPI.keyboardServices.keyPressed("\n");
                    }
                })
            };
        },

        /**
         * Create an empty button with specified width.
         * @param {Number} intWidth width of the empty button.
         * @param {Number} intHeight height of the empty button.
         * @return the object
         * <pre><code>
         // An empty button of width 2
         this.createEmptyButton(2);
         </pre></code>
         */
        createEmptyButton: function (intWidth, intHeight) {
            return {
                intInputWidth: intWidth || 1,
                intInputHeight: intHeight || 1,
                objElement: new UIElements.Keyboard.Key({
                    strButtonType: "empty"
                })
            }
        },

        /* --------------------------------- Private Methods -------------------------------------- */
        //Hi Soft
        createPagedCandidateList: function () {
            var startIndex = (this.candidatesPage - 1 ) * this.candidatesPageSize;
            var endIndex = this.candidatesPage * this.candidatesPageSize;

            var pageCandidates = endIndex > this.arrCandidates.length ?
                this.arrCandidates.slice(startIndex) : this.arrCandidates.slice(startIndex, endIndex);

            var candidateButtons = dojo.query("li", this.domIMEOtherList);
            for (var x = 0; x < candidateButtons.length; x++) {
                if (x >= pageCandidates.length) {
                    candidateButtons[x].style.display = "none";
                    continue;
                }
                candidateButtons[x].innerHTML = pageCandidates[x];
                candidateButtons[x].style.display = "";
            }

            //clear page button
            this.domIMEPageUp.style.display = this.candidatesPage > 1 ? "" : "none";
            this.domIMEPageDown.style.display = endIndex < this.arrCandidates.length ? "" : "none";
            pageCandidates = null;
        },
        //Hi Soft
        /**
         * @private
         */
        recreateCandidateList: function (arrNewCandidates) {
            if (null != arrNewCandidates) this.arrCandidates = arrNewCandidates;
            // Display the top IME bar according to if the candidates list is empty
            if (arrNewCandidates || this.domIMEInput.value)this.setIMEVisible(true);
            this.candidatesPage = 1;
            // get this.candidatesPage page candidate list
            this.createPagedCandidateList();
        },
        //Hi Soft
        /**
         * @private
         */
        clearIMEInput: function () {
//            var field = this.domIMEInput;
//            field.selectionStart = 0;
//            field.selectionEnd = field.value.length;
//            this.deleteIMEChar();
            this.domIMEInput.value = "";
            this.setIMEVisible(false);
            ICTouchAPI.IMEServices.resetCharSequence();

        },
        //Hi Soft
        /**
         * @private
         */
        deleteIMEChar: function () {
            if (!this.domIMEInput.value) return false;
            this.domIMEInput.value = this.domIMEInput.value.substring(0, this.domIMEInput.value.length - 1);
            if (!this.domIMEInput.value)this.setIMEVisible(false);
            return true;
        },
        //Hi Soft
        /**
         * set dom IME Area visible
         * */
        setIMEVisible: function (visible) {
            var styleStr = visible ? "block" : "none";
            dojo.style(this.domIME, "display", styleStr);
        },

        setIMEString: function(str){
            this.domIMEInput.value = str;
            this.setIMEVisible(str);
        },
        /**
         * @private
         */
        addIMEChar: function (strChar) {
            var field = this.domIMEInput;
            field.value += strChar;
            this.setIMEVisible(field.value);
        },

        //Hi Soft
        pushInputZoneIntoInputField: function (clearInputZone) {
            var currentValue = this.domIMEInput.value;
            var inputField = this.getInputField();
            for (var index in currentValue) {
                inputField.addNewChar(currentValue[index]);
            }

            if (clearInputZone)this.clearIMEInput();
        },
        //Hi Soft
        displayCharInInputField: function (ch, clearInputZone) {
            for (var i = 0; i < ch.length; ++i) {
                this.getInputField().addNewChar(ch[i]);
            }
            if (clearInputZone) this.clearIMEInput();
        },

        /**
         * @private
         */
        _loadEmoMap: function (keyMap) {
            var that = this;
            // Load key maps from file.
            keyMap = keyMap || this.strLang;
            dojo.xhrGet({
                url: "library/ICTouchAPI/UIElements/Keyboard/keyMaps/" + keyMap + ".json",
                handleAs: "json",
                sync: true,

                load: function (response) {
                    that.arrEmoIcon = response.arrEmoIcon;

                },

                error: function (response, ioArgs) {
                    console.error(ioArgs);
                }
            });
        },
        //Hi Soft
        _populateIndicatorLabelText: function (strLang) {
            var selectedLangLabel = _("indicator_" + strLang, this.i18n).getTranslation();
            var toSwitchLang = 'apac_en' == strLang ? ICTouchAPI.keyboardServices.getLastLang() : 'apac_en';
            var toSwitchLangLabel = _("indicator_" + toSwitchLang, this.i18n).getTranslation();
            return (selectedLangLabel + "/").bold() + toSwitchLangLabel.sub().fontcolor("grey");
        },
        //Hi Soft
        updateIMEIndicator: function () {
//            create ime indicator button
            var isAPACIMELang = ICTouchAPI.keyboardServices.isAPACIMELang(this.strLang);
            this.imeIndicatorLabel = isAPACIMELang ? this._populateIndicatorLabelText(this.strLang) : "";
            var displayIndicator = isAPACIMELang && !this._isPasswordKeyBoard && this.strType == ICTouchAPI.KeyboardTypes.ALPHANUM;
            this._replaceButton(1, displayIndicator, this.createIMEIndicator);

            },
        setIMEStuff: function (strLang) {
            var isAPACIMELang = ICTouchAPI.keyboardServices.isAPACIMELang(this.strLang);
//            console.log("key type =" +this.strType);
            this.updateIMEIndicator();
            this.clearIMEInput();
            this.recreateCandidateList("");
            if (this.strType == ICTouchAPI.KeyboardTypes.ALPHANUM && !this._isPasswordKeyBoard) {
                ICTouchAPI.IMEServices.changeLanguage(strLang);
            }else{
                ICTouchAPI.keyboardServices.setIMERequirement(false);
            }
        },

        /**
         * @private
         */
        _gotDefaultKeyboard: function (defaultKeyboard) {

            if (defaultKeyboard) {
                this.strLang = defaultKeyboard.allowedValues[defaultKeyboard.jsValue].name;
                this.setIMEStuff(this.strLang);
                }
            else
                console.warn("KeyboardControl: setting Defaultkeyboard not provided.");
            this._loadKeyMaps();
            if(this.strLang == "cyrillic_russian" && this.strType == ICTouchAPI.KeyboardTypes.ALPHANUM)
                this._replaceButton(1,true,this.convertToKeyboardLetter);
            //replace button back to letter button when cyrillic_russian
            // Nothing to replace if nothing was there before
            if (this.arrKeys.length > 0) {
                this._swapKeyMap();
            }
        },
        //for conflict
        convertToKeyboardLetter: function () {
            // Save scope for callback.
            var that = this;
            var BTNcontent = this.arrDefault[this.strLang][1] ? this.arrDefault[this.strLang][1][0] : "";
            this.objButton = {
                intInputWidth: 1,
                intInputHeight: 1,
                objElement: new UIElements.Keyboard.Key({
                    strButtonType: 'letter',
                    strButtonText: BTNcontent,
                    strButtonName: "alphaNum32",
                    boolAutorepeat: false,
                    // Both needed because the size of the icon ( 32 or 64 ) is computed on creation
                    intWidth: 1,
                    intHeight: 1,
                    funcCallback: function (value) {
                        that.keyboardLetterCallback(value);
                    },
                    funcCallbackRelease: function (value) {
                        ICTouchAPI.keyboardServices.keyReleased(value);
                    }
                }),
                swapContent: function (strText, strEmo) {
                        var boolEmo = strEmo != null && strEmo != '';
                        if (this.emoticon == false) {
                            if (boolEmo) {
                                this.objElement.replaceText('');
                                this.objElement.setIcon(strEmo)
                            }
                            else {
                                this.objElement.replaceText(strText);
                            }
                        } else {
                            if (boolEmo) {
                                this.objElement.replaceText('');
                                this.objElement.replaceIcon(strEmo);
                            } else {
                                this.objElement.removeIcon();
                                this.objElement.replaceText(strText);
                            }
                        }
                        this.strValue = strText;
                        this.emoticon = boolEmo;
                    }
                };

            return this.objButton;
        },
        /**
         * Replace a button at position
         * if custom is true, create a button using the function provided or else create an empty button
         * @private
         */
        _replaceButton: function (position, custom, functionRef, objButton) {
            var oldkey = this.arrKeys[position];
            var width = oldkey.intInputWidth || 1;
            var height = oldkey.intInputHeight || 1;
            this.arrKeys[position].objElement.destroy();
            if (custom) {
                if (functionRef) {
                    this.arrKeys[position] = functionRef.call(this, {
                        intInputHeight: height,
                        intInputWidth: width
                    });
                }
                else if (objButton) {
                    this.arrKeys[position] = this.createButton({
                        intInputWidth: 1,
                        intInputHeight: 1,
                        strButtonText: objButton.strButtonLabel,
                        strButtonIcon: objButton.strButtonIcon,
                        strButtonName: objButton.strButtonName,
                        strCustomClass: objButton.strCustomClass,
                        boolAttachParent: true,
                        callbackClick: objButton.callback
                    });
                }
            }
            else {
                this.arrKeys[position] = this.createEmptyButton(width, height);
            }
            this.arrKeys[position].objElement.placeAt(this.domArrKeys[position], "only");
        },


        /**
         * Initialize ok and exit labels
         * @private
         */
        _setOkExitLabels: function (strOkLabel, strExitLabel) {
            this.strOkLabel = strOkLabel || _("ok", this.i18n);
            this.strExitLabel = strExitLabel || _("Cancel", this.i18n);
        },

        /**
         * @private
         */
        _setOkExitIcons: function (strOkIcon, strExitIcon) {
            this.strOkIcon = strOkIcon || "generic-ok";
            this.strExitIcon = strExitIcon || "generic-cancel";
        },

        /**
         * @private
         */
        _loadKeyMaps: function (keyMap) {
            var that = this;
            // Load key maps from file.
            keyMap = keyMap || this.strLang;
            if (!this.languages[keyMap]) {
                dojo.xhrGet({
                    url: "library/ICTouchAPI/UIElements/Keyboard/keyMaps/" + keyMap + ".json",
                    handleAs: "json",
                    sync: true,

                    load: function (response) {
                        that.arrStar = response.arrStar;
                        that.arrDefault = response.arrDefault;
                        that.languages[keyMap] = response;
                    },

                    error: function (response, ioArgs) {
                        console.error(ioArgs);
                    }
                });
            } else {
                this.arrStar = this.languages[keyMap].arrStar;
                this.arrDefault = this.languages[keyMap].arrDefault;
            }
        },
        //Hi Soft
        /**
         * @private
         */
        _candidateSelected: function (args) {
            ICTouchAPI.keyboardServices.candidateSelected(args.toElement.innerText);
            this.clearIMEInput();

        },
        _candidatePageUp: function (args) {
            --this.candidatesPage;
            this.createPagedCandidateList();

        },
        //Hi Soft
        _candidatePageDown: function (args) {
            ++this.candidatesPage;
            this.createPagedCandidateList();
        },

        _callbackEmoticon: function (context) {
            if (!this._flags["emoticon"] && !this._flags["emoticonLock"]) {
                this._flags["emoticon"] = true;
            }
            else if (this._flags["emoticon"] && !this._flags["emoticonLock"]) {
                this._flags["emoticonLock"] = true;
            }
            else if (this._flags["emoticon"] && this._flags["emoticonLock"]) {
                this._flags["emoticon"] = false;
                this._flags["emoticonLock"] = false;
            }
            this._swapKeyMap();
            this._updateButtonsIcons();
        },

        /**
         * Toggle flags
         * @private
         * @param {bool} flag the flag to turn on/off
         * @return the _flags object
         */
        _toggleFlag: function (flag) {
            this._flags[flag] = this._flags[flag] == 0 ? 1 : 0;
            return this._flags;
        },

        /**
         * @private
         * Turn all flags to off but specified flags.
         * @param {List} butFlag a list of flags that should not be modified.
         * @return the _flags array.
         */
        _allFlagsOff: function (butFlag) {
            butFlag = butFlag || [];
            butFlag.join(",");
            for (var flag in this._flags) {
                if (butFlag.indexOf(flag, 0) == -1) {
                    this._flags[flag] = 0;
                }
            }
            return this._flags;
        },

        /**
         * @private
         */
        _swapKeyMap: function (strLetter) {
            // No swap possible when type is num-pad.
            if (this.strType === "num")
                return;
            var i, _tmpKeys;
            strLetter = strLetter || this._flags["starKey"];
            // If STAR + Key
            if (this._flags["star"] && strLetter) {
                // If there's a replacement keyMap
                if (this.arrStar["" + strLetter.toLowerCase()]) {
                    // Pick up the new keyMap to display.
                    _tmpKeys = this.arrStar["" + strLetter.toLowerCase()];
                }
                else {
                    // No swap to do as no keyMap is defined for this key: set the flags back to default and return
                    this._flags["star"] = 0;
                    this._flags["starKey"] = "";
                    return;
                }
                // If no STAR + Key corresponding, load the default key map.
            } else {
                if (this._flags["numLock"]) {
                    _tmpKeys = this.arrDefault["arrNum"];
                    if (this._flags["emoticon"]) {
                        _tmpKeys = this.arrEmoIcon;
                    }
                }
                else {
                    _tmpKeys = this.arrDefault[this.strLang];
                }
            }

            // Replace keys.
//            for (i in _tmpKeys) {
//                this.arrKeys[i].swapContent(_tmpKeys[i][this._flags["caps"]], _tmpKeys[i][2 + this._flags["caps"]]);
//            }
            //CSimon: logic has been reversed so that keys that are not defined in the Json layout file are replaced by ""
            // instead of simply keeping the previous value.
            // Therefore, it is no longer needed to define each and every keep in the Json files.
            for (i=0; i<this.arrKeys.length; ++i) {
                if (this.arrKeys[i].swapContent) {
                    if (_tmpKeys[i] === undefined) {
                        this.arrKeys[i].swapContent("", "");
                    } else {
                        this.arrKeys[i].swapContent(_tmpKeys[i][this._flags["caps"]], _tmpKeys[i][2+this._flags["caps"]]);
                    }
                }
            }
        },

        /**
         * @private
         * Keyboard's skeleton that must be completed with getKeyMap
         * @param {String} type whether "alphaNum" or "dialpad"
         * @return {list} the list of keys that compose the skeleton.
         * @private
         */
        _getKeyboardSkeleton: function (type) {
            var that = this;
            if (type === "alphaNum") {
                // Class to add on the domNode after templating
                this._strClassToAdd = "alphaNum";
                return  [
                    this.createButton({
                        strButtonText: '123',
                        strNotification: 'notif-off',
                        strButtonName: 'keyboard_NUM',
                        callback: function () {
                            if (that._flags["numLock"] === 0) {
                                that._flags["numLock"] = 1;
                                that._flags["star"] = 0;
                            } else if (that._flags["numLock"] === 1) {
                                that._flags["numLock"] = 0;
                                that._flags["emoticon"] = 0;
                                that._flags["emoticonLock"] = 0;
                            }
                            that._swapKeyMap();
                            that._updateButtonsIcons();
                        }
                    }),
                    //this.createEmptyButton(1, 1), // Hi Soft Input method indicator
                    // the keyboard letter at position #1, is not always there in the layout json file, so protect ourselves from "undefined"
                    this.createKeyboardLetter('alphaNum32', this.arrDefault[this.strLang][1] ? this.arrDefault[this.strLang][1][0] : ""),
                    this.createInputField({
                        intInputWidth: 9,
                        strInputText: this.strInputText,
                        boolPassword: this.keyboardFlags.password,
                        hideStarsIfPasswordEmpty: this.hideStarsIfPasswordEmpty
                    }),
//                    this.createEmptyButton(1, 1),
                    //the keyboard letter at position #3, is not always there in the layout json file, so protect ourselves from "undefined"
                    this.createKeyboardLetter('alphaNum33', this.arrDefault[this.strLang][3] ? this.arrDefault[this.strLang][3][0] : ""),
                    this.createButton({
                        strButtonIcon: 'language',
                        strButtonName: 'keyboard_FR',
                        callback: function () {
                            ICTouchAPI.keyboardServices.switchKeyboard();
                        }
                    }),
                    this.createButton({
                        strNotification: 'notif-off',
                        strButtonText: _('Alt', this.i18n),
                        strButtonName: 'keyboard_STAR',
                        callback: function () {
                            if (that._flags["numLock"] === 1) {
                                that._callbackEmoticon();
                            }
                            else {
                                that._toggleFlag("star");
                                dojo.publish("UIElement.keyboard.star", [that._flags["star"]]);
                                if (!that._flags["star"]) {
                                    if (that._flags["starKey"] != "") {
                                        that._flags["starKey"] = ""
                                    }
                                    that._swapKeyMap();
                                }
                            }
                            that._updateButtonsIcons();
                        }
                    }),
                    this.createKeyboardLetter('alphaNum1', this.arrDefault[this.strLang][6][0]),
                    this.createKeyboardLetter('alphaNum2', this.arrDefault[this.strLang][7][0]),
                    this.createKeyboardLetter('alphaNum3', this.arrDefault[this.strLang][8][0]),
                    this.createKeyboardLetter('alphaNum4', this.arrDefault[this.strLang][9][0]),
                    this.createKeyboardLetter('alphaNum5', this.arrDefault[this.strLang][10][0]),
                    this.createKeyboardLetter('alphaNum6', this.arrDefault[this.strLang][11][0]),
                    this.createKeyboardLetter('alphaNum7', this.arrDefault[this.strLang][12][0]),
                    this.createKeyboardLetter('alphaNum8', this.arrDefault[this.strLang][13][0]),
                    this.createKeyboardLetter('alphaNum9', this.arrDefault[this.strLang][14][0]),
                    this.createKeyboardLetter('alphaNum10', this.arrDefault[this.strLang][15][0]),
                    this.createKeyboardLetter('alphaNum11', this.arrDefault[this.strLang][16][0]),
                    this.createEmptyButton(1, 1), // Delete button
                    this.createButton({
                        strButtonIcon: 'caps',
                        strNotification: 'notif-off',
                        strButtonName: 'keyboard_CAPS',
                        callback: function () {
                            if (that._flags["caps"] === 0 &&
                                that._flags["capsLock"] === 0) {
                                that._flags["caps"] = 1;
                            } else if (that._flags["caps"] === 1 &&
                                that._flags["capsLock"] === 0) {
                                that._flags["capsLock"] = 1;
                            } else if (that._flags["caps"] === 1 &&
                                that._flags["capsLock"] === 1) {
                                that._flags["caps"] = 0;
                                that._flags["capsLock"] = 0;
                            }
                            that._swapKeyMap();
                            that._updateButtonsIcons();
                        }
                    }),
                    this.createKeyboardLetter('alphaNum12', this.arrDefault[this.strLang][19][0]),
                    this.createKeyboardLetter('alphaNum13', this.arrDefault[this.strLang][20][0]),
                    this.createKeyboardLetter('alphaNum14', this.arrDefault[this.strLang][21][0]),
                    this.createKeyboardLetter('alphaNum15', this.arrDefault[this.strLang][22][0]),
                    this.createKeyboardLetter('alphaNum16', this.arrDefault[this.strLang][23][0]),
                    this.createKeyboardLetter('alphaNum17', this.arrDefault[this.strLang][24][0]),
                    this.createKeyboardLetter('alphaNum18', this.arrDefault[this.strLang][25][0]),
                    this.createKeyboardLetter('alphaNum19', this.arrDefault[this.strLang][26][0]),
                    this.createKeyboardLetter('alphaNum20', this.arrDefault[this.strLang][27][0]),
                    this.createKeyboardLetter('alphaNum21', this.arrDefault[this.strLang][28][0]),
                    this.createKeyboardLetter('alphaNum22', this.arrDefault[this.strLang][29][0]),
                    this.createEmptyButton(1, 1), // Carriage return
                    this.createEmptyButton(1, 1), // Exit button
                    this.createKeyboardLetter('alphaNum23', this.arrDefault[this.strLang][32][0]),
                    this.createKeyboardLetter('alphaNum24', this.arrDefault[this.strLang][33][0]),
                    this.createKeyboardLetter('alphaNum25', this.arrDefault[this.strLang][34][0]),
                    this.createKeyboardLetter('alphaNum26', this.arrDefault[this.strLang][35][0]),
                    this.createSpaceBar({
                        strButtonName: 'alphaNum27'
                    }),
                    this.createKeyboardLetter('alphaNum28', this.arrDefault[this.strLang][37][0]),
                    this.createKeyboardLetter('alphaNum29', this.arrDefault[this.strLang][38][0]),
                    this.createKeyboardLetter('alphaNum30', this.arrDefault[this.strLang][39][0]),
                    this.createKeyboardLetter('alphaNum31', this.arrDefault[this.strLang][40][0]),
                    this.createEmptyButton(1, 1) // Ok button
                ];
            }

            else if (type === "num") {
                // Class to add on the domNode after templating
                this._strClassToAdd = "dialpad";

                return [
                    this.createInputField({
                        intInputWidth: 5,
                        intInputHeight: 1,
                        strInputText: this.strInputText
                    }),

                    this.createEmptyButton(1, 1),
                    (this._objSpace = this.createSpace()), //DialPad1
                    this.createKeyboardLetter('dialPad2', "2", "abc"),
                    this.createKeyboardLetter('dialPad3', "3", "def"),
                    this.createEmptyButton(1, 1),

                    this.createEmptyButton(1, 1),
                    this.createKeyboardLetter('dialPad4', "4", "ghi"),
                    this.createKeyboardLetter('dialPad5', "5", "jkl"),
                    this.createKeyboardLetter('dialPad6', "6", "mno"),
                    this.createEmptyButton(1, 1),

                    this.createEmptyButton(1, 1),
                    this.createKeyboardLetter('dialPad7', "7", "pqrs"),
                    this.createKeyboardLetter('dialPad8', "8", "tuv"),
                    this.createKeyboardLetter('dialPad9', "9", "wxyz"),
                    this.createEmptyButton(1, 1),

                    this.createEmptyButton(1, 1),
                    this.createKeyboardLetter('dialPad10', "*"),
                    (this._objInternational = this.createInternational()),
                    this.createKeyboardLetter('dialPad12', "#"),
                    this.createEmptyButton(1, 1),

                    this.createEmptyButton(1, 1), // Exit button
                    this.createEmptyButton(3, 1), // Ok button
                    this.createEmptyButton(1, 1)  // Delete button
                ];
            }
            else {
                console.warn("No keyboard loaded", type);
            }
            return [];
        },

        /**
         * @private
         */
        _updateButtonsIcons: function () {
            var len = this.arrKeys.length;
            for (var i = 0; i < len; ++i) {
                var button = this.arrKeys[i].objElement;
                switch (button.strButtonName) {
                    case 'keyboard_STAR':
                        if (this._flags["numLock"]) {
                            button.replaceText(_("emo", this.i18n));
                            button.replaceIcon("im-emote-bigsmile");
                        } else {
                            button.replaceText(_("Alt", this.i18n));
                            button.removeIcon();
                        }
                        if (this._flags["star"] || this._flags["emoticonLock"])
                            button.attr("strNotification", "notif-on");
                        else
                            button.attr("strNotification", "notif-off");
                        break;
                    case 'keyboard_NUM':
                        if (this._flags["numLock"])
                            button.attr("strNotification", "notif-on");
                        else
                            button.attr("strNotification", "notif-off");
                        break;
                    case 'keyboard_CAPS':
                        if (this._flags["capsLock"])
                            button.attr("strNotification", "notif-on");
                        else
                            button.attr("strNotification", "notif-off");
                        break;
                }
            }
        },

        /**
         * Block the event propagation so the InputField doesn't lose focus
         * But don't block the click on actual field
         * @private
         */
        _block: function (e) {
            var tagname = e.target.tagName.toLowerCase();
            if (tagname !== "input" && tagname !== "textarea") {
                dojo.stopEvent(e);
            }
        },

        /**
         * @private
         */
        keyboardLetterCallback: function (buttonText) {
            // If an extended keyboard is required.
            if (this._flags["star"]) {
                // Is it already displayed?
                if (!this._flags["starKey"]) {
                    // If not, set a flag with the desired keyboard (defined by a letter).
                    this._flags["starKey"] = buttonText;
                    // Swap the keyboard.
                    this._swapKeyMap(buttonText);
                } else {
                    // If the extended keyboard is already displayed
                    ICTouchAPI.keyboardServices.keyPressed(buttonText);
                    // Set the flags back to default so it won't be displayed after keypress.
                    this._flags["star"] = 0;
                    this._flags["starKey"] = "";
                    if (this._flags["caps"] === 1 && this._flags["capsLock"] === 0) {
                        this._flags["caps"] = 0;
                    }
                    // Swap the keyboard back to default.
                    this._swapKeyMap();
                }
                this._updateButtonsIcons();
            } else {
                // If no extended keyboard required or displayed.
                ICTouchAPI.keyboardServices.keyPressed(buttonText);
                // Swap back if no caps lock
                if (this._flags["caps"] === 1 && this._flags["capsLock"] === 0) {
                    this._flags["caps"] = 0;
                    // Swap the keyboard back to default.
                    this._swapKeyMap();
                    this._updateButtonsIcons();
                }
                // Swap back if no num lock
                /*if (this._flags["num"] === 1 && this._flags["numLock"] === 0) {
                 this._flags["num"] = 0;
                 // Swap the keyboard back to default.
                 this._swapKeyMap();
                 this._updateButtonsIcons();
                 }*/
            }
        },

        _setDefaultModeFlags: function () {
            this._flags["caps"] = 0;
            this._flags["star"] = 0;
            this._flags["emoticon"] = 0;
            this._flags["emoticonLock"] = 0;
            this._flags["capsLock"] = 0;
            this._flags["starKey"] = "";
            //this._flags["num"] = 0;
            this._flags["numLock"] = 0;
            this._flags["multiLines"] = false;
            this._flags["ime"] = 0;
        },
        //Hi Soft
        _imeIndicatorCallback: function () {
            /**
             * this.imeIndicatorState 0: EN else localeIME
             * */
            var nextLang = ICTouchAPI.keyboardServices.getNextLang(this.strLang);
            ICTouchAPI.keyboardServices.switchKeyboardFromIMEIndicator(nextLang);
        },
        /**
         * @private
         */
        _backspaceCallback: function () {
            var imeRequired = ICTouchAPI.keyboardServices._IMERequired;
            ICTouchAPI.keyboardServices.delChars(imeRequired);
        },

        /**
         * Get next position for the buttons in dialpad mode
         * @private
         */
        _getNextPosition: function (flag, oldposition) {
            if (!flag) {
                return oldposition;
            }

            if (oldposition >= 23) {
                return -1;
            }
            else if (oldposition >= 20) {
                return 23;
            }
            else {
                return oldposition + 5;
            }
        },

        /**
         * Get next position for the buttons in dialpad mode
         * @private
         */
        _getPreviousPosition: function (flag, oldposition) {
            if (!flag) {
                return oldposition;
            }

            if (oldposition > 20) {
                return 20;
            }
            else if (oldposition <= 5) {
                return -1;
            }
            else {
                return oldposition - 5;
            }
        }


    });
